package com.kingmed.kmss.admin.modules.sys.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kingmed.kmss.admin.modules.sys.mapper.SysFileMapper;
import com.kingmed.kmss.admin.modules.sys.model.SysFile;
import com.kingmed.kmss.admin.modules.sys.service.ISysFileService;
import com.kingmed.kmss.admin.modules.sys.vo.ImageVo;
import com.kingmed.kmss.common.constant.common.OperatorConstants;
import com.kingmed.kmss.common.exception.BusinessException;
import com.kingmed.kmss.common.service.obs.HuaWeiYunOBSService;
import com.kingmed.kmss.common.utils.DateTimeUtil;
import com.kingmed.kmss.common.utils.kingmed.FileUtils;
import com.kingmed.kmss.common.utils.StringUtil;
import com.kingmed.kmss.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 文件信息 服务实现类
 * </p>
 *
 * @author csw
 * @since 2023-04-17
 */
@Slf4j
@Service
public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> implements ISysFileService {

    @Autowired
    private HuaWeiYunOBSService huaWeiYunOBSService;

    /**
     * 文件上传
     * @param uploadFile
     * @param filePath
     * @param fileType
     * @return
     * @throws Exception
     */
    @Override
    public SysFile upload(MultipartFile uploadFile, String filePath, String fileType) throws Exception {
        Map<String, String> resultMap = huaWeiYunOBSService.fileUpload(uploadFile, filePath);
        return saveSysFile(uploadFile.getContentType(), uploadFile.getSize()+"", fileType, resultMap);
    }

    /**
     * 文件上传
     * @param uploadFile
     * @param fileType
     * @return
     * @throws Exception
     */
    @Override
    public SysFile upload(MultipartFile uploadFile, String fileType) throws Exception {
        return upload(uploadFile, DateTimeUtil.datePath().concat(OperatorConstants.SLASH_KEY), fileType);
    }

    /**
     * 文件上传
     * @param imageVo
     * @return
     */
    @Override
    public SysFile upload(ImageVo imageVo) {
        if (null == imageVo || StringUtils.isBlank(imageVo.getBase64Img())){
            throw new BusinessException("上传文件失败");
        }
        if (FileUtils.isNotImageFromBase64(imageVo.getBase64Img())){
            throw new BusinessException("图片不合法");
        }
        Map<String, String> resultMap = null;
        if (StringUtils.isBlank(imageVo.getUploadPath())) {
            resultMap = huaWeiYunOBSService.fileUpload(imageVo.getBase64Img(), DateTimeUtil.datePath().concat(OperatorConstants.SLASH_KEY));
        }else {
            resultMap = huaWeiYunOBSService.fileUpload(imageVo.getBase64Img(), imageVo.getUploadPath());
        }
        if (CollUtil.isEmpty(resultMap)){
            throw new BusinessException("上传文件失败");
        }
        return saveSysFile(resultMap.get("contentType"), resultMap.get("fileSize"), imageVo.getFileBizType(), resultMap);
    }

    /**
     * 文件下载
     * @param response
     * @param fileId
     */
    @Override
    public void downLoadAuth(HttpServletResponse response, String fileId) {
        SysFile sysFile = this.getById(fileId);
        if(sysFile == null){
            throw new BusinessException("文件不存在");
        }
        // 响应头处理
        responseHeaderDeal(response, sysFile);

        InputStream is = null;
        OutputStream outputStream  = null;
        try {
            is = huaWeiYunOBSService.fileDownload(sysFile.getRelativePath());
            outputStream = response.getOutputStream();
            // 文件流回写
            byte[] buf = new byte[1024];
            int len;
            while ((len = is.read(buf)) > 0) {
                outputStream.write(buf, 0, len);
            }
            outputStream.flush();

            response.flushBuffer();
            log.info("downloadLaster end fileId:{}", sysFile.getFileId());
        } catch (IOException e) {
            log.error("文件下载异常, fileId:{}", sysFile.getFileId(), e);
        }finally {
            try{
                if(is != null){
                    is.close();
                    is = null;
                }
            }catch (Exception e){
                log.error("关闭流异常", e);
            }

            try{
                if (outputStream != null) {
                    outputStream.close();
                    outputStream = null;
                }
            }catch (Exception e){
                log.error("关闭流异常", e);
            }
        }
    }

    /**
     * 文件批量下载并压缩成zip
     * @param fileIds
     * @param response
     */
    @Override
    public void downLoadBatchToZip(List<String> fileIds, HttpServletResponse response) throws Exception{
        if (CollUtil.isEmpty(fileIds)){
            throw new BusinessException("文件不存在");
        }
        List<SysFile> sysFiles = this.listByIds(fileIds);
        if(CollUtil.isEmpty(sysFiles)){
            throw new BusinessException("文件不存在");
        }
        List<String> paths = sysFiles.stream().map(SysFile::getRelativePath).collect(Collectors.toList());
        huaWeiYunOBSService.downLoadBatchToZip(paths, response);
    }

    /**
     * 响应请求头、文件名处理
     * @param response
     * @param sysFile
     */
    private static void responseHeaderDeal(HttpServletResponse response, SysFile sysFile) {
        String realFileName = StringUtil.encodeUtf8(sysFile.getName());
        // 非常重要
        response.reset();
        response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
        String fileType = sysFile.getFileType();
        response.setContentType(fileType);
        if(StringUtils.isNotBlank(fileType) && ( fileType.contains("image") || fileType.contains("pdf"))){
            // 统一下载
            response.setHeader("Content-Disposition", "attachment;filename=" + realFileName);
        } else if (fileType.contains("video")) {
            response.setHeader("Accept-Ranges", "bytes");
            response.setHeader("Content-Length", String.valueOf(sysFile.getFileSize()));
        } else {
            response.setHeader("Content-Disposition", "attachment;filename=" + realFileName);
        }
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setContentLength(Integer.parseInt(sysFile.getFileSize()));
    }

    /**
     * 文件保存
     * @param contentType
     * @param size
     * @param fileBizType
     * @param resultMap
     * @return
     */
    private SysFile saveSysFile(String contentType, String size, String fileBizType, Map<String, String> resultMap) {
        SysFile sysFile = new SysFile();
        sysFile.setBizType(fileBizType);
        sysFile.setName(resultMap.get("fileName"));
        sysFile.setNewName(resultMap.get("obsFileName"));
        sysFile.setFileType(contentType);
        sysFile.setFileSize(size);
        sysFile.setRelativePath(sysFile.getNewName());
        this.save(sysFile);
        return sysFile;
    }

    /**
     * 文件删除
     * @param fileId
     * @return
     */
    @Override
    public boolean delete(String fileId) {
        SysFile sysFile = this.getById(fileId);
        if (null == sysFile){
            throw new BusinessException("文件不存在");
        }
        if (huaWeiYunOBSService.delete(sysFile.getRelativePath())){
            return this.removeById(fileId);
        }
        return false;
    }

    /**
     * 文件删除
     * @param fileIds
     * @return
     */
    @Override
    public boolean delete(List<String> fileIds) {
        List<SysFile> sysFiles = this.listByIds(fileIds);
        if (CollUtil.isEmpty(sysFiles)){
            throw new BusinessException("文件不存在");
        }
        List<String> filePaths = sysFiles.stream().map(SysFile::getRelativePath).collect(Collectors.toList());
        if (huaWeiYunOBSService.delete(filePaths)){
            return this.removeByIds(fileIds);
        }
        return false;
    }
}
